; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -loop-reduce -S | FileCheck %s

target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n64-S128"
target triple = "riscv64"


define void @icmp_zero(i64 %N, ptr %p) {
; CHECK-LABEL: @icmp_zero(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[VECTOR_BODY:%.*]]
; CHECK:       vector.body:
; CHECK-NEXT:    [[LSR_IV:%.*]] = phi i64 [ [[LSR_IV_NEXT:%.*]], [[VECTOR_BODY]] ], [ [[N:%.*]], [[ENTRY:%.*]] ]
; CHECK-NEXT:    store i64 0, ptr [[P:%.*]], align 8
; CHECK-NEXT:    [[LSR_IV_NEXT]] = add i64 [[LSR_IV]], -2
; CHECK-NEXT:    [[DONE:%.*]] = icmp eq i64 [[LSR_IV_NEXT]], 0
; CHECK-NEXT:    br i1 [[DONE]], label [[EXIT:%.*]], label [[VECTOR_BODY]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br label %vector.body

vector.body:
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %vector.body ]
  store i64 0, ptr %p
  %iv.next = add i64 %iv, 2
  %done = icmp eq i64 %iv.next, %N
  br i1 %done, label %exit, label %vector.body

exit:
  ret void
}

define void @icmp_zero_urem_nonzero_con(i64 %N, ptr %p) {
; CHECK-LABEL: @icmp_zero_urem_nonzero_con(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[UREM:%.*]] = urem i64 [[N:%.*]], 16
; CHECK-NEXT:    br label [[VECTOR_BODY:%.*]]
; CHECK:       vector.body:
; CHECK-NEXT:    [[LSR_IV:%.*]] = phi i64 [ [[LSR_IV_NEXT:%.*]], [[VECTOR_BODY]] ], [ [[UREM]], [[ENTRY:%.*]] ]
; CHECK-NEXT:    store i64 0, ptr [[P:%.*]], align 8
; CHECK-NEXT:    [[LSR_IV_NEXT]] = add i64 [[LSR_IV]], -2
; CHECK-NEXT:    [[DONE:%.*]] = icmp eq i64 [[LSR_IV_NEXT]], 0
; CHECK-NEXT:    br i1 [[DONE]], label [[EXIT:%.*]], label [[VECTOR_BODY]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %urem = urem i64 %N, 16
  br label %vector.body

vector.body:
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %vector.body ]
  store i64 0, ptr %p
  %iv.next = add i64 %iv, 2
  %done = icmp eq i64 %iv.next, %urem
  br i1 %done, label %exit, label %vector.body

exit:
  ret void
}

define void @icmp_zero_urem_invariant(i64 %N, i64 %M, ptr %p) {
; CHECK-LABEL: @icmp_zero_urem_invariant(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[UREM:%.*]] = urem i64 [[N:%.*]], [[M:%.*]]
; CHECK-NEXT:    br label [[VECTOR_BODY:%.*]]
; CHECK:       vector.body:
; CHECK-NEXT:    [[LSR_IV:%.*]] = phi i64 [ [[LSR_IV_NEXT:%.*]], [[VECTOR_BODY]] ], [ [[UREM]], [[ENTRY:%.*]] ]
; CHECK-NEXT:    store i64 0, ptr [[P:%.*]], align 8
; CHECK-NEXT:    [[LSR_IV_NEXT]] = add i64 [[LSR_IV]], -2
; CHECK-NEXT:    [[DONE:%.*]] = icmp eq i64 [[LSR_IV_NEXT]], 0
; CHECK-NEXT:    br i1 [[DONE]], label [[EXIT:%.*]], label [[VECTOR_BODY]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %urem = urem i64 %N, %M
  br label %vector.body

vector.body:
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %vector.body ]
  store i64 0, ptr %p
  %iv.next = add i64 %iv, 2
  %done = icmp eq i64 %iv.next, %urem
  br i1 %done, label %exit, label %vector.body

exit:
  ret void
}

; We have to be careful here as SCEV can only compute a subtraction from
; two pointers with the same base.  If we hide %end inside a unknown, we
; can no longer compute the subtract.
define void @icmp_zero_urem_invariant_ptr(i64 %N, i64 %M, ptr %p) {
; CHECK-LABEL: @icmp_zero_urem_invariant_ptr(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[UREM:%.*]] = urem i64 [[N:%.*]], [[M:%.*]]
; CHECK-NEXT:    [[END:%.*]] = getelementptr i64, ptr [[P:%.*]], i64 [[UREM]]
; CHECK-NEXT:    br label [[VECTOR_BODY:%.*]]
; CHECK:       vector.body:
; CHECK-NEXT:    [[IV:%.*]] = phi ptr [ [[P]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[VECTOR_BODY]] ]
; CHECK-NEXT:    store i64 0, ptr [[P]], align 8
; CHECK-NEXT:    [[IV_NEXT]] = getelementptr i64, ptr [[IV]], i64 1
; CHECK-NEXT:    [[DONE:%.*]] = icmp eq ptr [[IV_NEXT]], [[END]]
; CHECK-NEXT:    br i1 [[DONE]], label [[EXIT:%.*]], label [[VECTOR_BODY]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %urem = urem i64 %N, %M
  %end = getelementptr i64, ptr %p, i64 %urem
  br label %vector.body

vector.body:
  %iv = phi ptr [ %p, %entry ], [ %iv.next, %vector.body ]
  store i64 0, ptr %p
  %iv.next = getelementptr i64, ptr %iv, i64 1
  %done = icmp eq ptr %iv.next, %end
  br i1 %done, label %exit, label %vector.body

exit:
  ret void
}

; Negative test - We can not hoist because we don't know value of %M.
define void @icmp_zero_urem_nohoist(i64 %N, i64 %M, ptr %p) {
; CHECK-LABEL: @icmp_zero_urem_nohoist(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[VECTOR_BODY:%.*]]
; CHECK:       vector.body:
; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[VECTOR_BODY]] ]
; CHECK-NEXT:    store i64 0, ptr [[P:%.*]], align 8
; CHECK-NEXT:    [[IV_NEXT]] = add i64 [[IV]], 2
; CHECK-NEXT:    [[UREM:%.*]] = urem i64 [[N:%.*]], [[M:%.*]]
; CHECK-NEXT:    [[DONE:%.*]] = icmp eq i64 [[IV_NEXT]], [[UREM]]
; CHECK-NEXT:    br i1 [[DONE]], label [[EXIT:%.*]], label [[VECTOR_BODY]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br label %vector.body

vector.body:
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %vector.body ]
  store i64 0, ptr %p
  %iv.next = add i64 %iv, 2
  %urem = urem i64 %N, %M
  %done = icmp eq i64 %iv.next, %urem
  br i1 %done, label %exit, label %vector.body

exit:
  ret void
}

define void @icmp_zero_urem_nonzero(i64 %N, i64 %M, ptr %p) {
; CHECK-LABEL: @icmp_zero_urem_nonzero(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[NONZERO:%.*]] = add nuw i64 [[M:%.*]], 1
; CHECK-NEXT:    [[UREM:%.*]] = urem i64 [[N:%.*]], [[NONZERO]]
; CHECK-NEXT:    br label [[VECTOR_BODY:%.*]]
; CHECK:       vector.body:
; CHECK-NEXT:    [[LSR_IV:%.*]] = phi i64 [ [[LSR_IV_NEXT:%.*]], [[VECTOR_BODY]] ], [ [[UREM]], [[ENTRY:%.*]] ]
; CHECK-NEXT:    store i64 0, ptr [[P:%.*]], align 8
; CHECK-NEXT:    [[LSR_IV_NEXT]] = add i64 [[LSR_IV]], -2
; CHECK-NEXT:    [[DONE:%.*]] = icmp eq i64 [[LSR_IV_NEXT]], 0
; CHECK-NEXT:    br i1 [[DONE]], label [[EXIT:%.*]], label [[VECTOR_BODY]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %nonzero = add nuw i64 %M, 1
  %urem = urem i64 %N, %nonzero
  br label %vector.body

vector.body:
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %vector.body ]
  store i64 0, ptr %p
  %iv.next = add i64 %iv, 2
  %done = icmp eq i64 %iv.next, %urem
  br i1 %done, label %exit, label %vector.body

exit:
  ret void
}

define void @icmp_zero_urem_vscale(i64 %N, ptr %p) {
; CHECK-LABEL: @icmp_zero_urem_vscale(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[VSCALE:%.*]] = call i64 @llvm.vscale.i64()
; CHECK-NEXT:    [[UREM:%.*]] = urem i64 [[N:%.*]], [[VSCALE]]
; CHECK-NEXT:    br label [[VECTOR_BODY:%.*]]
; CHECK:       vector.body:
; CHECK-NEXT:    [[LSR_IV:%.*]] = phi i64 [ [[LSR_IV_NEXT:%.*]], [[VECTOR_BODY]] ], [ [[UREM]], [[ENTRY:%.*]] ]
; CHECK-NEXT:    store i64 0, ptr [[P:%.*]], align 8
; CHECK-NEXT:    [[LSR_IV_NEXT]] = add i64 [[LSR_IV]], -2
; CHECK-NEXT:    [[DONE:%.*]] = icmp eq i64 [[LSR_IV_NEXT]], 0
; CHECK-NEXT:    br i1 [[DONE]], label [[EXIT:%.*]], label [[VECTOR_BODY]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %vscale = call i64 @llvm.vscale.i64()
  %urem = urem i64 %N, %vscale
  br label %vector.body

vector.body:
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %vector.body ]
  store i64 0, ptr %p
  %iv.next = add i64 %iv, 2
  %done = icmp eq i64 %iv.next, %urem
  br i1 %done, label %exit, label %vector.body

exit:
  ret void
}

define void @icmp_zero_urem_vscale_mul8(i64 %N, ptr %p) {
; CHECK-LABEL: @icmp_zero_urem_vscale_mul8(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[VSCALE:%.*]] = call i64 @llvm.vscale.i64()
; CHECK-NEXT:    [[MUL:%.*]] = mul nuw nsw i64 [[VSCALE]], 8
; CHECK-NEXT:    [[UREM:%.*]] = urem i64 [[N:%.*]], [[MUL]]
; CHECK-NEXT:    br label [[VECTOR_BODY:%.*]]
; CHECK:       vector.body:
; CHECK-NEXT:    [[LSR_IV:%.*]] = phi i64 [ [[LSR_IV_NEXT:%.*]], [[VECTOR_BODY]] ], [ [[UREM]], [[ENTRY:%.*]] ]
; CHECK-NEXT:    store i64 0, ptr [[P:%.*]], align 8
; CHECK-NEXT:    [[LSR_IV_NEXT]] = add i64 [[LSR_IV]], -2
; CHECK-NEXT:    [[DONE:%.*]] = icmp eq i64 [[LSR_IV_NEXT]], 0
; CHECK-NEXT:    br i1 [[DONE]], label [[EXIT:%.*]], label [[VECTOR_BODY]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %vscale = call i64 @llvm.vscale.i64()
  %mul = mul nuw nsw i64 %vscale, 8
  %urem = urem i64 %N, %mul
  br label %vector.body

vector.body:
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %vector.body ]
  store i64 0, ptr %p
  %iv.next = add i64 %iv, 2
  %done = icmp eq i64 %iv.next, %urem
  br i1 %done, label %exit, label %vector.body

exit:
  ret void
}


define void @icmp_zero_urem_vscale_mul64(i64 %N, ptr %p) {
; CHECK-LABEL: @icmp_zero_urem_vscale_mul64(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[VSCALE:%.*]] = call i64 @llvm.vscale.i64()
; CHECK-NEXT:    [[MUL:%.*]] = mul nuw nsw i64 [[VSCALE]], 64
; CHECK-NEXT:    [[UREM:%.*]] = urem i64 [[N:%.*]], [[MUL]]
; CHECK-NEXT:    br label [[VECTOR_BODY:%.*]]
; CHECK:       vector.body:
; CHECK-NEXT:    [[LSR_IV:%.*]] = phi i64 [ [[LSR_IV_NEXT:%.*]], [[VECTOR_BODY]] ], [ [[UREM]], [[ENTRY:%.*]] ]
; CHECK-NEXT:    store i64 0, ptr [[P:%.*]], align 8
; CHECK-NEXT:    [[LSR_IV_NEXT]] = add i64 [[LSR_IV]], -2
; CHECK-NEXT:    [[DONE:%.*]] = icmp eq i64 [[LSR_IV_NEXT]], 0
; CHECK-NEXT:    br i1 [[DONE]], label [[EXIT:%.*]], label [[VECTOR_BODY]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %vscale = call i64 @llvm.vscale.i64()
  %mul = mul nuw nsw i64 %vscale, 64
  %urem = urem i64 %N, %mul
  br label %vector.body

vector.body:
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %vector.body ]
  store i64 0, ptr %p
  %iv.next = add i64 %iv, 2
  %done = icmp eq i64 %iv.next, %urem
  br i1 %done, label %exit, label %vector.body

exit:
  ret void
}

define void @icmp_zero_urem_vscale_shl3(i64 %N, ptr %p) {
; CHECK-LABEL: @icmp_zero_urem_vscale_shl3(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[VSCALE:%.*]] = call i64 @llvm.vscale.i64()
; CHECK-NEXT:    [[SHL:%.*]] = shl i64 [[VSCALE]], 3
; CHECK-NEXT:    [[UREM:%.*]] = urem i64 [[N:%.*]], [[SHL]]
; CHECK-NEXT:    br label [[VECTOR_BODY:%.*]]
; CHECK:       vector.body:
; CHECK-NEXT:    [[LSR_IV:%.*]] = phi i64 [ [[LSR_IV_NEXT:%.*]], [[VECTOR_BODY]] ], [ [[UREM]], [[ENTRY:%.*]] ]
; CHECK-NEXT:    store i64 0, ptr [[P:%.*]], align 8
; CHECK-NEXT:    [[LSR_IV_NEXT]] = add i64 [[LSR_IV]], -2
; CHECK-NEXT:    [[DONE:%.*]] = icmp eq i64 [[LSR_IV_NEXT]], 0
; CHECK-NEXT:    br i1 [[DONE]], label [[EXIT:%.*]], label [[VECTOR_BODY]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %vscale = call i64 @llvm.vscale.i64()
  %shl = shl i64 %vscale, 3
  %urem = urem i64 %N, %shl
  br label %vector.body

vector.body:
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %vector.body ]
  store i64 0, ptr %p
  %iv.next = add i64 %iv, 2
  %done = icmp eq i64 %iv.next, %urem
  br i1 %done, label %exit, label %vector.body

exit:
  ret void
}

define void @icmp_zero_urem_vscale_shl6(i64 %N, ptr %p) {
; CHECK-LABEL: @icmp_zero_urem_vscale_shl6(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[VSCALE:%.*]] = call i64 @llvm.vscale.i64()
; CHECK-NEXT:    [[SHL:%.*]] = shl i64 [[VSCALE]], 6
; CHECK-NEXT:    [[UREM:%.*]] = urem i64 [[N:%.*]], [[SHL]]
; CHECK-NEXT:    br label [[VECTOR_BODY:%.*]]
; CHECK:       vector.body:
; CHECK-NEXT:    [[LSR_IV:%.*]] = phi i64 [ [[LSR_IV_NEXT:%.*]], [[VECTOR_BODY]] ], [ [[UREM]], [[ENTRY:%.*]] ]
; CHECK-NEXT:    store i64 0, ptr [[P:%.*]], align 8
; CHECK-NEXT:    [[LSR_IV_NEXT]] = add i64 [[LSR_IV]], -2
; CHECK-NEXT:    [[DONE:%.*]] = icmp eq i64 [[LSR_IV_NEXT]], 0
; CHECK-NEXT:    br i1 [[DONE]], label [[EXIT:%.*]], label [[VECTOR_BODY]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %vscale = call i64 @llvm.vscale.i64()
  %shl = shl i64 %vscale, 6
  %urem = urem i64 %N, %shl
  br label %vector.body

vector.body:
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %vector.body ]
  store i64 0, ptr %p
  %iv.next = add i64 %iv, 2
  %done = icmp eq i64 %iv.next, %urem
  br i1 %done, label %exit, label %vector.body

exit:
  ret void
}

; Loop invariant does not neccessarily mean dominating the loop.  Forming
; an ICmpZero from this example would be illegal even though the operands
; to the compare are loop invariant.
define void @loop_invariant_definition(i64 %arg) {
; CHECK-LABEL: @loop_invariant_definition(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[T1:%.*]]
; CHECK:       t1:
; CHECK-NEXT:    [[LSR_IV:%.*]] = phi i64 [ [[LSR_IV_NEXT:%.*]], [[T1]] ], [ -1, [[ENTRY:%.*]] ]
; CHECK-NEXT:    [[LSR_IV_NEXT]] = add nsw i64 [[LSR_IV]], 1
; CHECK-NEXT:    br i1 true, label [[T4:%.*]], label [[T1]]
; CHECK:       t4:
; CHECK-NEXT:    [[T5:%.*]] = trunc i64 [[LSR_IV_NEXT]] to i32
; CHECK-NEXT:    [[T6:%.*]] = add i32 [[T5]], 1
; CHECK-NEXT:    [[T7:%.*]] = icmp eq i32 [[T5]], [[T6]]
; CHECK-NEXT:    ret void
;
entry:
  br label %t1

t1:                                                ; preds = %1, %0
  %t2 = phi i64 [ %t3, %t1 ], [ 0, %entry ]
  %t3 = add nuw i64 %t2, 1
  br i1 true, label %t4, label %t1

t4:                                                ; preds = %1
  %t5 = trunc i64 %t2 to i32
  %t6 = add i32 %t5, 1
  %t7 = icmp eq i32 %t5, %t6
  ret void
}

declare i64 @llvm.vscale.i64()
